{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FYLyuStTYesc"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "PVm-iEoxYesf"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3MPf91rVYesq"
      },
      "source": [
        "# Grappler による TensorFlow グラフ最適化"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zmNCsZlgYesr"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/guide/graph_optimization\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org で表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/guide/graph_optimization.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/guide/graph_optimization.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/guide/graph_optimization.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l0qacLgyYess"
      },
      "source": [
        "## 概要\n",
        "\n",
        "TensorFlow は、グラフ実行と Eager Execution の両方を使用して計算を実行します。`tf.Graph`には、計算の単位を表す`tf.Operation`オブジェクト（演算）と、演算間を流れるデータの単位を表す`tf.Tensor`オブジェクトのセットが含まれています。\n",
        "\n",
        "Grappler は、TensorFlow ランタイム内のデフォルトのグラフ最適化システムです。Grappler は、グラフモード（`tf.function`内）で最適化を適用して、グラフの簡略化や、プロシージャ間の最適化を可能にする関数本体のインライン化などその他の高レベルの最適化により、TensorFlow 計算のパフォーマンスを向上させます。`tf.Graph`を最適化すると、グラフノードの計算リソースへのマッピングを最適化することによってデバイスのピークメモリ使用量が削減され、ハードウェアの使用率が向上します。\n",
        "\n",
        "`tf.Graph`の最適化をより細かく制御するには、`tf.config.optimizer.set_experimental_options()`を使用します。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "A-zkJgR5Yesw"
      },
      "source": [
        "## 使用可能なグラフオプティマイザ\n",
        "\n",
        "Grappler は`MetaOptimizer`と呼ばれるトップレベルのドライバを通してグラフの最適化を実行します。TensorFlowでは、次のグラフオプティマイザの使用が可能です。\n",
        "\n",
        "- *定数折り畳みオプティマイザ -* 可能な場合にグラフの定数ノードを折り畳むことによりテンソルの値を静的に推測し、定数を使用して結果をマテリアライズします。\n",
        "- *算術オプティマイザ -* 一般的な副次式を消去し、算術ステートメントを簡略化することにより、算術演算を簡略化します。\n",
        "- *レイアウトオプティマイザ -* テンソルのレイアウトを最適化することにより、畳み込みなどのデータフォーマットに依存する演算をより効率的に実行します。\n",
        "- *リマッパーオプティマイザ -* 一般的に発生するサブグラフを最適化された結合モノリシックカーネルで置き換えることにより、サブグラフをより効率的な実装に再マッピングします。\n",
        "- *メモリオプティマイザ -* グラフを分析して各演算のピークメモリ使用量を確認し、CPU-GPU メモリコピー演算を挿入してGPUメモリをCPUのメモリにスワップすることにより、ピークメモリ使用量を減らします。\n",
        "- *依存オプティマイザ -* 制御の依存関係を削除または並べ替えすることにより、モデルステップのクリティカルパスを短縮、または他の最適化を有効化します。 Identity などの実質的な NoOp ノードも削除します。\n",
        "- *プルーニングオプティマイザ -* グラフからの出力に影響を与えないノードを削除します。通常はこれを最初に実行することにより、グラフのサイズを縮小化し、他の Grappler パスの処理を高速化します。\n",
        "- *関数オプティマイザ -* TensorFlow プログラムの関数ライブラリを最適化し、関数本体をインライン展開することにより、他のプロシージャ間最適化を可能にします。\n",
        "- *形状オプティマイザ -* 形状および形状関連の情報に関する演算のサブグラフを最適化します。\n",
        "- *自動並列オプティマイザ -* バッチの次元に沿って分割することにより、グラフを自動的に並列化します。このオプティマイザはデフォルトではオフです。\n",
        "- *ループオプティマイザ -* ループ内からループ不変のサブグラフを引き上げ、冗長なスタック演算をループ外に移動させることにより、グラフ制御フローを最適化します。また、静的に既知のトリップカウントを使用してループを最適化することにより、静的に既知の条件付き分岐のデッドブランチを削除します。\n",
        "- *スコープアロケーターオプティマイザ -* スコープアロケーターを導入することにより、データの移動を削減し、一部の演算を統合します。\n",
        "- *ホスト固定オプティマイザ -* 小さな演算を CPU にスワップします。このオプティマイザはデフォルトではオフです。\n",
        "- *自動混合精度オプティマイザ -* パフォーマンスを向上させるために、適用可能な場合にデータ型を float16 に変換します。現在、GPU にのみ適用されます。\n",
        "- *デバッグストリッパー -* `tf.debugging.Assert`、 `tf.debugging.check_numerics`、`tf.print`などのデバッグ演算関連のノードをストリップします。このオプティマイザはデフォルトではオフです。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WZAUsxyWYess"
      },
      "source": [
        "## セットアップ\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6BRIDzO6ypoY"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import timeit\n",
        "import traceback\n",
        "import contextlib\n",
        "\n",
        "\n",
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1O-XL1nxJX0X"
      },
      "source": [
        "オプティマイザの状態を簡単に切り替えるためのコンテキストマネージャを作成します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "uRuhVoAlYesz"
      },
      "outputs": [],
      "source": [
        "@contextlib.contextmanager\n",
        "def options(options):\n",
        "  old_opts = tf.config.optimizer.get_experimental_options()\n",
        "  tf.config.optimizer.set_experimental_options(options)\n",
        "  try:\n",
        "    yield\n",
        "  finally:\n",
        "    tf.config.optimizer.set_experimental_options(old_opts)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "E2o4kZtK0DoA"
      },
      "source": [
        "## Grappler 使用の有無による実行パフォーマンスを比較する\n",
        "\n",
        "TensorFlow 2 以降は、デフォルトで [Eager](../eager.md) Execution が実装されています。デフォルトの実行を Graph モードに切り替えるには、デフォルトで`tf.function`を使用します。Grappler はバックグラウンドで自動的に実行し、上記のグラフオプティマイザを適用して実行パフォーマンスを向上させます。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3sh8RoLJ96IT"
      },
      "source": [
        "### 定数折り畳みオプティマイザ\n",
        "\n",
        "導入的な例として、定数に対して演算を実行し、出力を返す関数を 1 つ考えます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jOW_OSzMJEvN"
      },
      "outputs": [],
      "source": [
        "def test_function_1():\n",
        "  @tf.function\n",
        "  def simple_function(input_arg):\n",
        "    print('Tracing!')\n",
        "    a = tf.constant(np.random.randn(2000,2000), dtype = tf.float32)\n",
        "    c = a\n",
        "    for n in range(50):\n",
        "      c = c@a\n",
        "    return tf.reduce_mean(c+input_arg)\n",
        "\n",
        "  return simple_function"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tFVgUhhzLKIo"
      },
      "source": [
        "定数折り畳みオプティマイザをオフにして、関数を実行します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "KDMGsOHrJqKD"
      },
      "outputs": [],
      "source": [
        "with options({'constant_folding': False}):\n",
        "  print(tf.config.optimizer.get_experimental_options())\n",
        "  simple_function = test_function_1()\n",
        "  # Trace once\n",
        "  x = tf.constant(2.2)\n",
        "  simple_function(x)\n",
        "  print(\"Vanilla execution:\", timeit.timeit(lambda: simple_function(x), number = 1), \"s\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ykMXfo8qO41z"
      },
      "source": [
        "定数折り畳みオプティマイザを有効にして関数を再度実行し、関数実行の高速化を観察します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "73pc0gfFKY8l"
      },
      "outputs": [],
      "source": [
        "with options({'constant_folding': True}):\n",
        "  print(tf.config.optimizer.get_experimental_options())\n",
        "  simple_function = test_function_1()\n",
        "  # Trace once\n",
        "  x = tf.constant(2.2)\n",
        "  simple_function(x)\n",
        "  print(\"Constant folded execution:\", timeit.timeit(lambda: simple_function(x), number = 1), \"s\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "83w8rfcRVhWb"
      },
      "source": [
        "### デバッグ ストリッパー オプティマイザ\n",
        "\n",
        "入力引数の数値をチェックし、それを返す単純な関数を見てみましょう。 "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "j2DvqEr8haut"
      },
      "outputs": [],
      "source": [
        "def test_function_2():\n",
        "  @tf.function\n",
        "  def simple_func(input_arg):\n",
        "    output = input_arg\n",
        "    tf.debugging.check_numerics(output, \"Bad!\")\n",
        "    return output\n",
        "  return simple_func"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ywKG3WRbpYB8"
      },
      "source": [
        "まず最初に、デバッグストリッパーオプティマイザをオフにして関数を実行します。 "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LsE-y6iQWSwH"
      },
      "outputs": [],
      "source": [
        "test_func = test_function_2()\n",
        "p1 = tf.constant(float('inf'))\n",
        "try:\n",
        "  test_func(p1)\n",
        "except tf.errors.InvalidArgumentError as e:\n",
        "  traceback.print_exc(limit=2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "URHpboM8xLN6"
      },
      "source": [
        "`test_func`に対する`Inf`引数のため、`tf.debugging.check_numerics`には無効な引数エラーが発生します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CuPSha9YmJRo"
      },
      "source": [
        "デバッグストリッパーオプティマイザを有効にして、関数を再度実行します。 "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "UPJ7ygHnWP6B"
      },
      "outputs": [],
      "source": [
        "with options({'debug_stripper': True}):\n",
        "  test_func2 = test_function_2()\n",
        "  p1 = tf.constant(float('inf'))\n",
        "  try:\n",
        "    test_func2(p1)\n",
        "  except tf.errors.InvalidArgumentError as e:\n",
        "    traceback.print_exc(limit=2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nAsESNUB1QpI"
      },
      "source": [
        "デバッグストリッパーオプティマイザがグラフから`tf.debug.check_numerics`ノードを取り除き、エラーを発生させることなく関数を実行します。 "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wHC6tR9GvFgW"
      },
      "source": [
        "## まとめ\n",
        "\n",
        "TensorFlow ランタイムは Grappler を使用して、実行前にグラフを自動的に最適化します。`tf.config.optimizer.set_experimental_options`を使用すると、様々なグラフオプティマイザを有効または無効にすることができます。\n",
        "\n",
        "Grappler に関する詳しい情報は、<a class=\"external\" href=\"http://web.stanford.edu/class/cs245/slides/TFGraphOptimizationsStanford.pdf\">TensorFlow グラフの最適化</a>をご覧ください。"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "graph_optimization.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
